package com.yss.adapter.template;

import com.alibaba.fastjson.JSON;
import com.yss.adapter.AdapterExtraInfo;
import com.yss.adapter.IAdapter;
import com.yss.exception.RemotingException;
import com.yss.exception.RemotingTimeOutException;
import com.yss.pojo.response.ResponseMessage;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.nio.charset.Charset;

/**
 * HttpClient
 */
public final class IRestAdapterTemplate extends IAdapter {

    private HttpPool httpPool = new HttpPool();

    @Override
    public Object handle(AdapterExtraInfo adapterExtraInfo) throws RemotingException, RemotingTimeOutException, IOException {
        return sendByRestHandle(adapterExtraInfo);
    }

    @Override
    public Object sendByRestHandle(AdapterExtraInfo adapterExtraInfo) throws IOException {
        IRestAdapterInfo iRestAdapterInfo = (IRestAdapterInfo) adapterExtraInfo;
        HttpRequestBase httpRequestBase;

        if (iRestAdapterInfo.getHttpGet() != null) {
            httpRequestBase = iRestAdapterInfo.getHttpGet();
        } else {
            httpRequestBase = iRestAdapterInfo.getHttpPost();
        }
        httpRequestBase.setConfig(iRestAdapterInfo.getRequestConfig());
        return httpPool.execute(httpRequestBase);
    }

    /**
     * Default Request Config
     */
    private static class HttpPool {
        private static int MaxTotal = 10000;
        private static int MaxPerRoute = 10000;

        private static int ConnectionTimeOut = 5 * 1000;
        private static int SocketTimeOut = 5 * 1000;
        private static int BorrowTimeOut = 5 * 100;

        private static RequestConfig defaultRequest;
        private static CloseableHttpClient httpclient;
        private static PoolingHttpClientConnectionManager poolManager = new PoolingHttpClientConnectionManager();

        static {
            // 每一个目标Route都有独立的连接池，并且池大小为MaxTotal，目的是为了做不同Route的连接池隔离，避免同一个连接耗尽了所有连接，导致出现类似线程饥饿的现象
            // @see org.apache.http.pool.AbstractConnPool.getPool()
            poolManager.setMaxTotal(MaxTotal);
            // 每一个Route最大并发数
            poolManager.setDefaultMaxPerRoute(MaxPerRoute);

            defaultRequest = RequestConfig.custom()
                    .setConnectTimeout(ConnectionTimeOut)
                    .setSocketTimeout(SocketTimeOut)
                    .setConnectionRequestTimeout(BorrowTimeOut)
                    .build();

            httpclient = HttpClients.custom()
                    .setConnectionManager(poolManager)
                    .setDefaultRequestConfig(defaultRequest)
                    .build();
        }

        public Object execute(HttpRequestBase httpRequestBase) throws IOException {
            // it will return client
            CloseableHttpResponse response = httpclient.execute(httpRequestBase);
            return resolveResponse(response);
        }

        private Object resolveResponse(CloseableHttpResponse response) throws IOException {
            int responseStatusCode = response.getStatusLine().getStatusCode();
            try {
                if (responseStatusCode == 200) {
                    // Net work is normal.
                    String result = EntityUtils.toString(response.getEntity(), Charset.defaultCharset());
                    return JSON.parseObject(result, ResponseMessage.class);
                }
            } finally {
                if (response != null) {
                    response.close();
                }
            }
            return ResponseMessage.error("business module may not be started.", responseStatusCode);
        }
    }

}
